#include "apue.h"
#include <pthread.h>
#include "practice_11.5.head.c"


struct __barrier_node {
    pthread_mutex_t b_pmt;
    pthread_cond_t b_pct;
    __pthread_barrier_t * b_pbt;    
    struct __barrier_node * b_next;
    struct __barrier_node * b_prev;
    unsigned int b_c; 
    unsigned int b_req;
};

struct __barrier_link {
    struct __barrier_node * b_head;   
};


static struct __barrier_link bl = {NULL};

void __pthread_barrier_init(__pthread_barrier_t *barrier, unsigned int count) {
    struct __barrier_node *node=  malloc(sizeof(struct __barrier_node));
    node->b_pbt= barrier;
    node->b_next= bl.b_head;
    node->b_prev= NULL;
    node->b_c= 0;
    node->b_req= count;
    pthread_mutex_init(&node->b_pmt, NULL);
    pthread_cond_init(&node->b_pct,NULL);

    if (node->b_next) {
        node->b_next->b_prev= node;
    }

    bl.b_head= node;
}

static struct __barrier_node * find_barrier_node(__pthread_barrier_t *barrier) {
    struct __barrier_node *bn;
    bn= bl.b_head;
    while (bn) {
        if (bn->b_pbt == barrier)
            break;
        bn= bn->b_next;
    }
    return bn;
}

void __pthread_barrier_wait(__pthread_barrier_t *barrier) {
    struct __barrier_node * bn= find_barrier_node(barrier);
    if (bn == NULL) {
        err_quit("barrier is NULL");
    }
    if (bn->b_req < 2) {
        return;
    }
    bn->b_c++;
    if (bn->b_c < bn->b_req) {
        pthread_mutex_lock(&bn->b_pmt);
        while (bn->b_c < bn->b_req)
            pthread_cond_wait(&bn->b_pct, &bn->b_pmt);
        pthread_mutex_unlock(&bn->b_pmt);

    } else {
        pthread_cond_broadcast(&bn->b_pct);
    }
}

void __pthread_barrier_destroy(__pthread_barrier_t *barrier) {
    struct __barrier_node * bn= find_barrier_node(barrier);
    if (bn == NULL)
        return;

    if (bn->b_next) 
        bn->b_next->b_prev= bn->b_prev;
    if (bn->b_prev)
        bn->b_prev->b_next= bn->b_next;
    if (bn == bl.b_head)
        bl.b_head= bn->b_next;

    pthread_mutex_destroy(&bn->b_pmt);
    pthread_cond_destroy(&bn->b_pct);
}
